
#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"
#include "fxcc/core/graph/common/Geometry.h"
#include "fxcc/core/graph/common/ModelImporter.h"
#include "fxcc/core/graph/common/EffectsLoader.h"
#include "fxcc/core/graph/common/ShaderLoader.h"
#include "fxcc/mono/CSharpImporter.h"
#include "fxcc/mono/CSharpLoader.h"
#include "fxcc/imgui/Editor.h"
#include "fxcc/core/AssertManager.h"
#include "fxcc/mono/Maps.h"

#include "mini_base.h"

using namespace fxcc::platform;
using namespace fxcc::graph::common;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main

extern "C" {
    __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}


struct SimpleApp : public BaseApp
{
public:

    std::shared_ptr<ImageMipmap> m_ImageMipMap;

    glm::vec4 color;

    fxcc::graph::common::Camera camera;

    float pxRange{ 4.0f };

    std::string m_Text;

    ModelImporter modelImporter;

    EffectsLoader effectLoader;

    ShaderLoader shaderLoader;

    CSharpImpoter csharpImporter;
    CSharpLoader csharpLoader;

    SimpleApp(const fxcc::platform::common::App::Desc& desc) : BaseApp(desc), color(1.0)
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }
        #ifdef _WIN32
        ImGuiIO& io = ImGui::GetIO();
        ImFont* font = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/simhei.ttf", 25.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
        //ImFont* font = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 25.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
#endif
#ifdef __linux__

#endif

        return true;
    };

    fxcc::graph::common::EffectsDesc m_EffectsDescs;


    bool InitResources()
    {
        camera.SetPosition(glm::vec3(5.0f));
        camera.LookAt(glm::vec3(0.0f));

        fxcc::Dir dir("Assert");
        modelImporter.LoadCache();
        csharpImporter.LoadCache();

        csharpLoader.InitMono();
        csharpImporter.AddDepPath("./assert/CSharp/base");
        csharpImporter.ImportDir(dir);

        modelImporter.ImportDir(dir);

        modelImporter.SaveCache();
        csharpImporter.SaveCache();
        
        shaderLoader.LoadDirectory(dir);
        
        OnLoad();

        return true;
    };

    std::string m_EffectsDescPath = "Assert/pbr.effects";

    fxcc::imgui::Editor editor;

    //static std::wstring UTF8ToWString(const std::string& utf8Str)
    //{
    //    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    //    return converter.from_bytes(utf8Str);
    //}
    void OnEdit() override
    {
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

        ImGui::Begin("Framebuffer");
        editor.AddFrameBuffer(m_RTJack->m_Textures[0]->GetTextureId());
        ImGui::End();

        ImGui::Begin("Effectdesc");
        editor.EditInst("EffectsDescFile", m_EffectsDescs);

        bool _Effect_Load_btn = ImGui::Button("_reload_");
        if (_Effect_Load_btn)
        {
            fxcc::File effectFile(m_EffectsDescPath, false);
            shaderLoader.LoadEffects(effectFile, true);

        }
        ImGui::End();

        ImGui::Begin("Demo Scene");
        editor.EditInst<DemoScene>("m_Scene", m_Scene);
        ImGui::End();

        ImGuiIO& io = ImGui::GetIO();

        if (ImGui::IsKeyReleased(ImGuiKey_S) && io.KeyCtrl)
        {
            OnSave();
        }
    }
    std::shared_ptr<fxcc::graph::opengl3::RenderTargetJack> m_RTJack;
    fxcc::yml::Serializer ymlser;
    const std::string m_MatViewPath = "mat.nmat";
    const std::string m_ScenePath = "demo.scene";

    void OnLoad()
    {
        ymlser.DecodePath<fxcc::graph::common::EffectsDesc>(m_EffectsDescPath, m_EffectsDescs);
        ymlser.DecodePath<DemoScene>(m_ScenePath, m_Scene);

        m_Scene.OnLoad();
    }
    void OnSave()
    {
        ztclog::info("On save");
        ymlser.EncodePath<fxcc::graph::common::EffectsDesc>(m_EffectsDescPath, m_EffectsDescs);
        ymlser.EncodePath<DemoScene>(m_ScenePath, m_Scene);

    }
    void OnDestroy() override
    {
        modelImporter.SaveCache();

        BaseApp::OnDestroy();
    }
    void OnFrameRender() override
    {

        glViewport(0, 0, m_Desc.m_Size.x, m_Desc.m_Size.y);
        glClearColor(color.x, color.y, color.z, color.w);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        m_RTJack->Begin();
        m_RTJack->ClearColor(color);
        m_RTJack->ClearBuffer(true, true);

        passData.Load(camera);
        fxcc::graph::opengl3::Effect3d::SetPassData(passData);

        for (auto& [key, effect] : shaderLoader.m_Effects)
        {
            RenderScene(key, effect.get());
        }
        
        m_RTJack->End();
    };
    DemoScene m_Scene;
    fxcc::graph::common::Effect3d::PassData passData;
    fxcc::graph::common::Effect3d::ObjData objData;

    void RenderScene(const std::string& key, fxcc::graph::opengl3::Effect3d* effect)
    {

        effect->Begin();
        effect->SetFixed();

        for (auto obj : m_Scene.m_GameObjects)
        {
            const auto& materialView = obj.m_MaterialView;

            auto mesh = obj.m_MeshMap.m_Value;
            if (!mesh)
                continue;

            if (materialView.m_EffectToken.m_Key == key)
            {
                objData.Load(obj.m_Transform);
                fxcc::graph::opengl3::Effect3d::SetObjData(objData);

                effect->UseMaterial(materialView);
               
                mesh->Bind();
                mesh->DrawElementsAuto();
            }
        }
        effect->End();
    }

    void OnFramebuffersize(int w, int h) override
    {
        BaseApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());
        fxcc::graph::opengl3::RenderTargetJack::Desc desc(1, { w, h, 3, 0 });
        m_RTJack = std::make_shared<fxcc::graph::opengl3::RenderTargetJack>(desc);


    }
    void OnUpdate() override
    {
        BaseApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        float deltaTime = m_GlfwlTimer.m_DeltaTime;

        //camera.FreeMove(m_Input, deltaTime);
        m_Scene.OnUpdate();
    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line& line)
        {
            if (line.m_Type != ztclog::Line::Type::_line_debug_)
            {
                std::cout << line.m_Text << std::endl;
            } });

            using _App = SimpleApp;
            using _Desc = fxcc::platform::common::App::Desc;

            _Desc desc;
            desc.m_Pos = glm::ivec2(100, 100);
            desc.m_Size = glm::ivec2(900, 600);
            desc.m_ScaleFactor = 1.0f;

            desc.m_Title = "material view render ";
            std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
            return _app->Run();
};
